// /////////////////////////////
//  Makumba, Makumba tag library
//  Copyright (C) 2000-2003 http://www.makumba.org
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
//  -------------
//  $Id$
//  $Name$
/////////////////////////////////////

package org.makumba.forms.html;

import java.util.Dictionary;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import org.makumba.HtmlUtils;
import org.makumba.Pointer;
import org.makumba.commons.StringUtils;
import org.makumba.commons.formatters.FieldFormatter;
import org.makumba.commons.formatters.RecordFormatter;
import org.makumba.commons.http.MakumbaServlet;
import org.makumba.commons.tags.MakumbaJspConfiguration;
import org.makumba.forms.tags.SearchFieldTag;
import org.makumba.providers.DataDefinitionProvider;
import org.makumba.providers.QueryProvider;

public class ptrEditor extends choiceEditor {

    static String[] _params = { "default", "empty", "type", "size", "labelSeparator", "elementSeparator", "nullOption",
            "forceInputStyle", "autoComplete", "orderBy", "where", "labelName" };

    static String[][] _paramValues = { null, null, { "hidden", "radio", "checkbox", "tickbox" }, null, null, null,
            null, SearchFieldTag.allowedSelectTypes, new String[] { "true", "false" }, null, null, null };

    public static final String DEFAULT_LABEL_NAME = "o";

    private String labelName = DEFAULT_LABEL_NAME;

    @Override
    public String[] getAcceptedParams() {
        return _params;
    }

    @Override
    public String[][] getAcceptedValue() {
        return _paramValues;
    }

    private static final class SingletonHolder {
        static FieldEditor singleton = new ptrEditor();
    }

    /** Don't use this, use getInstance() */
    protected ptrEditor() {
    }

    public static FieldFormatter getInstance() {
        return SingletonHolder.singleton;
    }

    @Override
    public void onStartup(RecordFormatter rf, int fieldIndex, Dictionary<String, Object> formatParams) {
        if (formatParams.get("labelName") != null) {
            labelName = (String) formatParams.get("labelName");
        } else {
            labelName = DEFAULT_LABEL_NAME;
        }

        ((RecordEditor) rf).db[fieldIndex] = ((RecordEditor) rf).database;
        Map<String, String> m = new HashMap<String, String>();

        ((RecordEditor) rf).query[fieldIndex] = m;
        String titleField = rf.dd.getFieldDefinition(fieldIndex).getTitleField();
        String titleExpr = labelName + "." + titleField;

        String choiceType = rf.dd.getFieldDefinition(fieldIndex).getPointedType().getName();

        String orderByExpr;
        if (formatParams.get("orderBy") != null) {
            orderByExpr = (String) formatParams.get("orderBy");
        } else {
            orderByExpr = titleExpr;
        }
        if (formatParams.get("orderByDirection") != null) {
            orderByExpr += " " + (String) formatParams.get("orderByDirection");
        }

        String where = "";
        if (formatParams.get("where") != null) {
            where = " WHERE " + formatParams.get("where");
        }

        String query = "SELECT " + labelName + " as " + labelName + ", " + titleExpr + " as title FROM " + choiceType
                + " " + labelName + where + " ORDER BY " + orderByExpr;
        m.put("oql", query);
        m.put("hql", query);
    }

    @Override
    public Object getOptions(RecordFormatter rf, int fieldIndex, Dictionary<String, Object> formatParams) {

        ChoiceSet c = (ChoiceSet) formatParams.get(ChoiceSet.PARAMNAME);
        if (c != null) {
            return c;
        }

        Vector<Dictionary<String, Object>> v = null;
        String queryLang = (String) formatParams.get("org.makumba.forms.queryLanguage");
        QueryProvider qp = QueryProvider.makeQueryRunner(((RecordEditor) rf).db[fieldIndex], queryLang);

        // Transaction dbc = TransactionProvider.getInstance().getConnectionTo(((RecordEditor) rf).db[fieldIndex]);
        try {
            // v = dbc.executeQuery(((RecordEditor) rf).query[fieldIndex], null);
            v = qp.execute(((RecordEditor) rf).query[fieldIndex].get(queryLang), null, 0, -1);
        } finally {
            qp.close();
        }
        c = new ChoiceSet();
        if (nullOption != null) {
            c.add("", nullOption, false, false);
        }
        for (Dictionary<String, Object> d : v) {
            Object ttl = d.get("title");
            if (ttl == null) {
                Pointer ptr = (Pointer) d.get(labelName);
                // FIXME ? maybe just displayed the field as untitled?
                // ttl = "untitled [" + ptr.toExternalForm() + "]";
                final StringBuilder msg = new StringBuilder(150);
                msg.append("Object ").append(ptr).append(" (external ID: ").append(ptr.toExternalForm()).append(
                    ") has a null value for the title-field (").append(ptr.getType()).append(".").append(
                    DataDefinitionProvider.getInstance().getDataDefinition(ptr.getType()).getTitleFieldName()).append(
                    "), and can't be displayed in the drop-down.\nEither make sure you have no null values in this field, or use a different field for the title display, using the '!title=' directive in the MDD.");
                throw new org.makumba.ProgrammerError(msg.toString());
            }
            c.add(d.get(labelName), ttl.toString(), false, false);
        }
        return c;
    }

    @Override
    public int getOptionsLength(RecordFormatter rf, int fieldIndex, Object opts) {
        return ((ChoiceSet) opts).size();
    }

    @Override
    public Object getOptionValue(RecordFormatter rf, int fieldIndex, Object options, int i) {
        return ((ChoiceSet) options).get(i).getValue();
    }

    @Override
    public String formatOptionValue(RecordFormatter rf, int fieldIndex, Object val) {
        if (val == Pointer.Null) {
            return "";
        }
        if (val instanceof String) {
            // for reloaded forms (via ControllerFilter) check needed
            // val is String when a null option is selected in mak:input (with mak:option)
            return (String) val;
        }
        return ((Pointer) val).toExternalForm();
    }

    @Override
    public String formatOptionValue(RecordFormatter rf, int fieldIndex, Object opts, int i, Object val) {
        return formatOptionValue(rf, fieldIndex, val);
    }

    @Override
    public String formatOptionTitle(RecordFormatter rf, int fieldIndex, Object options, int i) {
        return ((ChoiceSet) options).get(i).getTitle();
    }

    @Override
    public Object readFrom(RecordFormatter rf, int fieldIndex, org.makumba.commons.attributes.HttpParameters p,
            String suffix) {
        Object o = super.readFrom(rf, fieldIndex, p, suffix);
        if ("".equals(o)) {
            return null;
        }
        return o;
    }

    @Override
    public boolean isMultiple(RecordFormatter rf, int fieldIndex) {
        return false;
    }

    @Override
    public int getDefaultSize(RecordFormatter rf, int fieldIndex) {
        return 1;
    }

    @Override
    public String format(RecordFormatter rf, int fieldIndex, Object o, Dictionary<String, Object> formatParams) {
        boolean autoComplete = formatParams.get("autoComplete") != null
                && formatParams.get("autoComplete").equals("true");

        // we have to check whether we are not a setEditor
        if (autoComplete && !(this instanceof setEditor)) {
            return formatAutoComplete(rf, fieldIndex, o, formatParams);
        } else {
            return super.format(rf, fieldIndex, o, formatParams);
        }
    }

    private String formatAutoComplete(RecordFormatter rf, int fieldIndex, Object o,
            Dictionary<String, Object> formatParams) {

        String res = "", id = "", inputName = "";

        inputName = getInputName(rf, fieldIndex, formatParams);

        // TODO: add a hidden input with the right name and id
        // extend the JS method for autocomplete to write the selected value in the hidden input

        id = StringUtils.getParam("id", getExtraFormatting(rf, fieldIndex, formatParams));

        // we need to have a different id for the visible input field
        // dirty hack, because we get the id hardcoded in the extra formatting params
        String extraFormattingVisible = getExtraFormatting(rf, fieldIndex, formatParams);
        int cutIndex = extraFormattingVisible.indexOf("id=") + 4 + id.length();
        extraFormattingVisible = extraFormattingVisible.substring(0, cutIndex) + "_visible"
                + extraFormattingVisible.substring(cutIndex);

        res += "<input name=\"" + inputName + "_visible\" type=\"text\" value=\""
                + formatValue(rf, fieldIndex, o, formatParams) + "\" " + extraFormattingVisible
                + "autocomplete=\"off\">";

        res += "<input name=\"" + inputName + "\" type=\"hidden\" value=\""
                + formatValue(rf, fieldIndex, o, formatParams) + "\" "
                + getExtraFormatting(rf, fieldIndex, formatParams) + ">";

        // the second part of the auto-complete, i.e. the dropdown that appears

        res += "<div id=\"autocomplete_choices_" + id + "\" class=\"autocomplete\"></div>";

        res += "<script type=\"text/javascript\">MakumbaAutoComplete.AutoComplete(\"" + id + "\", \""
                + MakumbaJspConfiguration.getServletLocation(MakumbaServlet.AUTOCOMPLETE) + "\", \""
                + rf.dd.getFieldDefinition(fieldIndex).getOriginalFieldDefinition().getDataDefinition().getName()
                + "\", \"" + rf.dd.getFieldDefinition(fieldIndex).getOriginalFieldDefinition().getName()
                + "\", \"ptr\", \"" + (String) formatParams.get("org.makumba.forms.queryLanguage") + "\");</script>";

        return res;
    }

    /** Formats the value to appear in an input statement. */
    @Override
    public String formatValue(RecordFormatter rf, int fieldIndex, Object o, Dictionary<String, Object> formatParams) {
        String s = o == null ? null : HtmlUtils.string2html(o.toString());
        return resetValueFormat(rf, fieldIndex, s, formatParams);
    }
}
